﻿/*
 * Copyright (c) HiSilicon (Shanghai) Technologies Co., Ltd. 2023-2023. All rights reserved.
 */

#include "BurnCtrlJlinkWorker.h"
#include <QCoreApplication>
#include "DataConvertor.h"
using namespace BurnCtrlJlinkWorkerClass;

BurnCtrlJlinkWorker::BurnCtrlJlinkWorker(QObject *parent) : QObject(parent),
    proBurnStart(nullptr)
{
    infoArray.clear();
}

void BurnCtrlJlinkWorker::SlotEraseFlash()
{
    QString saveDir = QCoreApplication::applicationDirPath() + "/tempFileJlink";
    QString path = Global::GetAppConfigPath();
    path += "\\config_" + Global::ChipTypeIdToString(Global::currentChipTypeId);
    QSettings cfg(path  + "\\config.ini", QSettings::IniFormat);
    QFile file(saveDir + "/scripts.txt");
    FileWriteScripts(cfg, saveDir, file);
    int jLinkEraseFileNumber = cfg.value("JLinkEraseScripts/JLinkEraseFileNumber").toInt();
    if (jLinkEraseFileNumber > 10) { // max file num 10
        jLinkEraseFileNumber = 10; // max file num 10
    }
    QStringList cfgList = cfg.allKeys();
    QString program = cfg.value(CFG_JLINK_PATH).toString() + "\\JLink.exe";
    QStringList folder;
    QString infoStr = "";
    for (int j = 0; j < jLinkEraseFileNumber; j++) {
        if (proBurnStart == nullptr) {
            proBurnStart = new QProcess();
        }
        if (proBurnStart == nullptr) {
            continue;
        }
        for (int i = 0; i < cfgList.size(); i++) {
            if (!cfgList[i].contains("JLinkErase/")) {
                continue;
            }
            QString value = cfgList[i].split('/')[1];
            if (!CmdWhitelisting(QPair<QString, QString>(value, cfg.value(cfgList[i]).toString()))) {
                continue;
            }
            folder << "-" + value;
            if (cfgList[i] == "JLinkErase/JLinkScriptFile") {
                folder << (path + "\\JLinkScript" + "\\" + cfg.value(cfgList[i]).toStringList()[j]);
                continue;
            }
            folder << cfg.value(cfgList[i]).toString();
        }
        folder << (saveDir + "/scripts.txt");
        proBurnStart->start(program, folder);
        proBurnStart->waitForStarted();
        proBurnStart->waitForFinished();
        QByteArray array = proBurnStart->readAllStandardOutput();
        QString str = QString::fromLocal8Bit(array);
        file.remove();
        delete proBurnStart;
        proBurnStart = nullptr;
        if (str.isEmpty()) {
            emit SignalEraseFlashFaild();
            return;
        }
        infoStr += str;
    }
    emit SignalEraseFlashSuccess(infoStr);
    return;
}

void BurnCtrlJlinkWorker::FileWriteScripts(const QSettings &cfg, const QString &saveDir, QFile &file)
{
    QDir dir(saveDir);
    if (!dir.exists()) {
        dir.mkdir(saveDir);
    }
    if (!file.open(QIODevice::WriteOnly)) {
        emit SignalEraseFlashFaild();
        return;
    }
    QStringList scriptList = cfg.value("JLinkEraseScripts/scripts").toStringList();
    for (int i = 0; i < scriptList.size(); i++) {
        file.write(scriptList[i].toLatin1() + "\n");
    }
    file.close();
}

void BurnCtrlJlinkWorker::SlotBurnFlash()
{
    int oldPos = 0;
    int count = 0;
    for (auto it = infoArray.begin(); it != infoArray.end(); it++) {
        QString path = Global::GetAppConfigPath();
        path += "\\config_" + Global::ChipTypeIdToString(Global::currentChipTypeId);
        QSettings cfg(path  + "\\config.ini", QSettings::IniFormat);
        QString saveDir = QCoreApplication::applicationDirPath() + "/tempFileJlink";
        QStringList cfgList = cfg.allKeys();
        QDir dir(saveDir);
        if (!dir.exists()) {
            dir.mkdir(saveDir);
        }
        QFile file(saveDir + "/scripts.txt");
        BurnFlashScripts(file, it);
        if (proBurnStart == nullptr) {
            proBurnStart = new QProcess();
        }
        if (proBurnStart == nullptr) {
            continue;
        }
        QString program = cfg.value(CFG_JLINK_PATH).toString() + "\\JLink.exe";
        QStringList folder;
        BurnFlashFolder(folder, cfgList, path, cfg, saveDir);
        proBurnStart->start(program, folder);
        bool ret = proBurnStart->waitForStarted(120000); // 120000ms等待
        ret = proBurnStart->waitForFinished(120000); // 120000ms等待
        QByteArray array = proBurnStart->readAllStandardOutput();
        QString str = QString::fromLocal8Bit(array);
        emit SignalBurnFlashStr(str);
        delete proBurnStart;
        proBurnStart = nullptr;
        file.remove();
        QString successCodeStr = "Downloading file [" + it.value().path + "]...\r\nO.K.";
        if (array.size() == 0 || !str.contains(successCodeStr)) {
            emit SignalBurnFlashFaild();
            return;
        } else {
            BurnFlashSetProgress(oldPos, ++count);
            qDebug() << __FUNCTION__<< "O.K." <<endl;
        }
    }
    emit SignalBurnFlashSuccess();
}

void BurnCtrlJlinkWorker::BurnFlashFolder(QStringList &folder, const QStringList &cfgList, const QString &path,
    const QSettings &cfg, const QString &saveDir) const
{
    for (int j = 0; j < cfgList.size(); j++) {
        if (!cfgList[j].contains("JLinkBurn/")) {
            continue;
        }
        QString value = cfgList[j].split('/')[1];
        if (!CmdWhitelisting(QPair<QString, QString>(value, cfg.value(cfgList[j]).toString()))) {
            continue;
        }
        folder << "-" + value;
        if (cfgList[j] == "JLinkBurn/JLinkScriptFile") {
            folder << (((path + "\\JLinkScript") + "\\") + cfg.value(cfgList[j]).toString());
            continue;
        }
        folder << cfg.value(cfgList[j]).toString();
    }
    folder << (saveDir + "/scripts.txt");
}

void BurnCtrlJlinkWorker::BurnFlashSetProgress(int oldPos, int index)
{
    if (infoArray.size() == 0) {
        emit SignalSetProgressValue(0);
        return;
    }
    double pos = static_cast<double>(index * SHRT_MAX) / static_cast<double>(infoArray.size());
    if (pos >= oldPos) {
        emit SignalSetProgressValue(pos);
        oldPos = pos;
    }
}

void BurnCtrlJlinkWorker::BurnFlashScripts(QFile &file, QMap<int, BIN_INFO>::iterator index)
{
    QString path = Global::GetAppConfigPath();
    path += "\\config_" + Global::ChipTypeIdToString(Global::currentChipTypeId);
    QSettings cfg(path  + "\\config.ini", QSettings::IniFormat);
    QString saveDir = QCoreApplication::applicationDirPath() + "/tempFileJlink";
    if (!file.open(QIODevice::WriteOnly)) {
        emit SignalBurnFlashFaild();
        return;
    }
    QStringList scriptList = cfg.value("JLinkBurnScript/scripts").toStringList();
    QString loadBinPath = "loadbin ";
    loadBinPath += index.value().path + " 0x" +
                   QString::number(DataConvertor::Str2U32DecHex(index.value().burnAddr), 0x10) + "\n";
    qDebug() << loadBinPath;
    file.write("connect\n");
    file.write("h\n");
    for (int i = 0; i < scriptList.size(); i++) {
        file.write(scriptList[i].toLatin1() + "\n");
    }
    file.write(loadBinPath.toStdString().c_str());
    file.write("q\n");
    file.close();
}

void BurnCtrlJlinkWorker::SetInfoArray(const QMap<int, BIN_INFO> &info)
{
    this->infoArray = info;
}

void BurnCtrlJlinkWorker::StopProBurnStart() const
{
    if (proBurnStart != nullptr) {
        QProcess *pro = new QProcess();
        if (pro == nullptr) {
            return;
        }
        QString cmd = "taskkill -t -f /pid " + QString::number(proBurnStart->processId());
        qDebug() << __FUNCTION__ << cmd << endl;
        pro->startDetached(cmd);
        delete pro;
    }
}

bool BurnCtrlJlinkWorker::CmdWhitelisting(QPair<QString, QString> mapfolder) const
{
    QMap<QString, QStringList> whiteListing = {
        {"device", QStringList() << "RISC-V"},
        {"if", QStringList() << "SWD"},
        {"speed", QStringList() << "1000" << "4000"},
        {"JLinkScriptFile", QStringList() << "ConnectCore1.JLinkScript" <<
         "eflash0_chip_erase.JLinkScript,eflash1_chip_erase.JLinkScript"},
        {"autoconnect", QStringList() << "1"}
    };
    auto it = whiteListing.begin();
    for (; it != whiteListing.end(); it++) {
        if (mapfolder.first == it.key()) {
            break;
        }
    }
    if (it == whiteListing.end()) {
        return false;
    }
    auto it1 = it.value().begin();
    for (; it1 != it.value().end(); it1++) {
        if (mapfolder.second == *it1) {
            break;
        }
    }

    if (it1 == it.value().end()) {
        return false;
    }
    return true;
}
